모듈패턴? 클로져?

클로저를 활용하면 모듈을 정의하여 원하는 프로퍼티나 메서드를 캡슐화할 수 있습니다. ES2015 이전에 자바스크립트에는 모듈이라는 개념이 없었기 때문에 클로저를 사용하여 모듈을 정의하여 사용하였습니다.


클로저로 모듈을 생성하지 않고 전역 스코프에 필요한 값들을 정의해도 됩니다. 하지만 전역 변수에 정의된 값들이 많아지면 사용하는 전역 변수가 어디서 선언되었는지 찾기 어렵고 다른 라이브러리와 변수명이 충돌할 수 있습니다. 최악의 경우에는 어딘가에서 전역 변수의 값이 덮어씌워져 애플리케이션이 제대로 동작하지 않을 수도 있습니다. 애플리케이션에 잠재적인 버그를 심고 싶지 않다면 전역 스코프를 오염시키지 않는 것이 좋습니다.

function myModule() {
  let counter = 0

  function increment() {
    counter += 1
  }

  function decrement() {
    counter -= 1
  }

  function getCount() {
    return counter
  }

  return {
    increment,
    decrement,
    getCount,
  }
}

const myCounter = myModule()

myCounter.increment()
console.log(myCounter.getCount()) // 1
myCounter.decrement()
console.log(myCounter.getCount()) // 0

myModule() 함수는 increment(), decrement(), getCount 함수들을 객체로 만들어 실행 결과로써 반환합니다. 반환된 함수들은 기억한 렉시컬 스코프 체인에 의해 myModule() 함수의 스코프에 접근할 수 있습니다. 그렇기 때문에 세 함수를 사용하면 외부 스코프에서도 myModule() 함수 내부에 선언된 counter 변수에 접근하여 값을 변경하거나 조회하는 것이 가능합니다. 그리고 여기서 중요한 점은 myModule() 함수가 반환한 객체는 함수들에 대한 참조만 가지며 내부 변수 counter에 대한 접근은 불가능하다는 점입니다. 즉 counter 변수는 캡슐화되어 외부에서 접근할 수 없으며, 접근하고 싶다면 외부로 반환한 클로저 함수를 통해서만 접근할 수 있습니다. 이것이 클로저를 활용한 모듈 패턴입니다.

여기서 즉시 실행 함수를 모듈 패턴과 함께 사용하면 전역 스코프를 오염시키지 않고 모듈 객체를 만들수도 있습니다.

const cleanModule = (function() {
  let counter = 0

  function increment() {
    counter += 1
  }

  function decrement() {
    counter -= 1
  }

  function getCount() {
    return counter
  }

  return {
    increment,
    decrement,
    getCount,
  }
})()

myModule() 함수를 즉시 실행 함수로 변경하여 바로 실행하였습니다. 그리고 실행 결과로 반환된 모듈 객체를 cleanModule 변수에 할당하였습니다. 이렇게 즉시 실행 함수와 모듈 패턴을 함께 사용하면 전역 스코프를 오염시키지 않고도 myModule()과 같은 모듈 함수를 선언하여 객체를 생성할 수 있습니다.

모듈

모듈은 외부로 공개한 API를 통해서 상태를 변경하고 내부 구현에 대한 캡슐화 역할을 합니다. 각각의 모듈은 애플리케이션을 구성하는 단위가 되며, 모듈 단위로 재사용하여 불필요한 코드를 죽이고 유지보수성을 높일 수 있습니다.

자바스크립트 모듈의 진화

ES2015 이전의 자바스크립트에는 모듈이란 개념이 존재하지 않았습니다. 자바스크립트에서는 파일마다 독립적은 스코프를 갖는 것이 아니라 전역 스코프를 공유하기 때문에 다른 파일의 변수와 중복이 되거나 값을 덮어쓰는 문제가 발생하였습니다. 이러한 문제를 극복하기 위해 모듈패턴에서 보았던 다양한 패턴들을 사용하였습니다.

그리고 나온 ES모듈은 각 파일을 독립적인 스코프로 처리합니다. 내보내기를 한 식별자가 아니라면 해당 모듈내에서만 접근할 수 있으며 외부 모듈에서는 접근할 수 없습니다. 더이상 전역 스코프를 걱정하며 개발할 필요가 없어졌습니다. 모듈은 다른 모듈에 정의된 특정 변수나 함수를 불러올 수 있고, 반대로 자신의 모듈안에 정의된 식별자들을 내보낼 수 있습니다.

출처

기초부터 완성까지, 프런트엔드 책